package com.crcc.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class UMD5 {
	static Logger logger = LoggerFactory.getLogger(UMD5.class);
	protected static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	protected static MessageDigest messagedigest = null;
	private static final String ALGORITHM = "MD5";

	static {
		try {
			messagedigest = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			logger.error("error", e);

		}
	}

	/**
	 * 摘要Hex数组
	 */
	private static final char[] DIGIT = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	/**
	 * 得到消息摘要对象 〈功能详细描述〉
	 *
	 * @param algorithm
	 *            加密算法名 本方法使用MD5
	 * @return MessageDigest
	 */
	private static MessageDigest getDigest(String algorithm) {
		try {
			return MessageDigest.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
			// 相关算法不存在
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 得到转换后的MD5字符数组
	 *
	 * @param ib
	 * @return char[]
	 */
	private static char[] encodeHEX(byte[] ib) {

		int l = ib.length;

		char[] out = new char[l << 1];

		// two characters form the hex value.
		for (int i = 0, j = 0; i < l; i++) {
			out[j++] = DIGIT[(0xF0 & ib[i]) >>> 4];
			out[j++] = DIGIT[0x0F & ib[i]];
		}

		return out;
	}

	/**
	 * encode
	 *
	 * @param source
	 * @return
	 */
	private static byte[] encode(String source) {
		return getDigest(ALGORITHM).digest(source.getBytes());
	}

	/**
	 * 使用MD5加密字符串,返回的是加密后的大写
	 *
	 * @param codeStr
	 *            要加密的字符串
	 * @return 返回已经加密的字符串
	 */
	public static String encodeMD5(String codeStr) {
		return new String(encodeHEX(encode(codeStr.toUpperCase())));
	}

	/**
	 * 通用MD5加密字符串
	 * <p>
	 * 可根据isToUpperCase决定是否需要将字符串全部转为大写进行加密
	 *
	 * @param codeStr
	 *            String 要加密的字符串
	 * @param isToUpperCase
	 *            boolean 是否需要将字符串全部转为大写
	 * @return String 返回已经加密的字符串
	 */
	public static String encodeMD5(String codeStr, boolean isToUpperCase) {
		if (isToUpperCase) {
			codeStr = codeStr.toUpperCase();
		}

		byte[] bytes = encode(codeStr);
		String encryptStr = new String(encodeHEX(bytes));

		if (!isToUpperCase) {
			encryptStr = encryptStr.toLowerCase();
		}

		return encryptStr;
	}

	public static String getFileMD5String(File file) throws IOException {
		FileInputStream in = new FileInputStream(file);
		FileChannel ch = in.getChannel();
		MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
		messagedigest.update(byteBuffer);
		in.close();
		return bufferToHex(messagedigest.digest());
	}

	public static String getMD5String(String s) {
		return getMD5String(s.getBytes());
	}

	public static String getMD5String(byte[] bytes) {
		messagedigest.update(bytes);
		return bufferToHex(messagedigest.digest());
	}

	private static String bufferToHex(byte bytes[]) {
		return bufferToHex(bytes, 0, bytes.length);
	}

	private static String bufferToHex(byte bytes[], int m, int n) {
		StringBuffer stringbuffer = new StringBuffer(2 * n);
		int k = m + n;
		for (int l = m; l < k; l++) {
			appendHexPair(bytes[l], stringbuffer);
		}
		return stringbuffer.toString();
	}

	private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
		char c0 = hexDigits[(bt & 0xf0) >> 4];
		char c1 = hexDigits[bt & 0xf];
		stringbuffer.append(c0);
		stringbuffer.append(c1);
	}

	public static boolean checkPassword(String password, String md5PwdStr) {
		String s = getMD5String(password);
		return s.equals(md5PwdStr);
	}

	public static void main(String[] args) throws IOException {
		long begin = System.currentTimeMillis();
		String str = "123";
		String md5 = getMD5String(str);
		long end = System.currentTimeMillis();
	}

}